package game;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.RadialGradientPaint;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;


public class HexBoardImage {
  
  private final HexBoardPanel hexBoardPanel;
  
  // These hexes are drawn by paint. They do not contain game info.
  public final Polygon[][] hexes;  
    
  private double cellWidth;
  private double cellHeight;
  private double radius;
  
  public BufferedImage imageBuffer; 
  public Graphics2D bufferGraphics;

  private final HexBoard hexBoard;
  
  private Polygon mouseHex = null;
  
  public boolean initialized = false;

  
  public HexBoardImage (HexBoardPanel hexBoardPanel, HexBoard hexBoard) {
    this.hexBoardPanel = hexBoardPanel;
    this.hexBoard = hexBoard;
    this.hexes = new Polygon[hexBoardPanel.size][hexBoardPanel.size];
  }


  /** cellCenter[0] is x
   *  cellCenter[1] is y 
   * @param x
   * @param y
   * @return
   */
  private double[] cellCenters (int x, int y) {
    return new double[] {(x + (double) y / 2 + 1.5) * cellWidth + 20,
                         (y + 1.5) * (cellHeight * 3 / 4) + 20}; 
  }
  
  
  public void createHexes() {
    int size = hexBoard.getSize();
    for (int y = 0; y < size; y++) {
      for (int x = 0; x < size; x++) {
        hexes[x][y] = makeHex(cellCenters(x, y));
      }
    }
  }


  private void drawHexBorder(Polygon hex, Color color) {
    bufferGraphics.setColor(color);
    bufferGraphics.drawPolygon(hex);
  }


  private Paint getRadialPaint(double[] cellCenters, Color color) {
    return new RadialGradientPaint(
            new Point2D.Double(cellCenters[0], cellCenters[1]), 
            (float) radius*2, 
            new Point2D.Double(cellCenters[0]-radius, cellCenters[1]-radius), 
            new float[] {0, 0.57f, 1} , 
            new Color[] {Color.WHITE, color, Color.BLACK}, 
            CycleMethod.NO_CYCLE);
  }


  /** Draw a bright border around the mouseHex. It is a changed
   *  mouseHex or this wouldn't have been called. 
   *  So draw a black border around the old mouseHex.
   * @param mouseCell
   */
  public void highlightMouseCellBorder(Cell mouseCell) {
    // Draw a black  border around the old mouseHex.
    if (mouseHex != null) drawHexBorder(mouseHex, Color.BLACK);
    
    if (mouseCell == null || !hexBoard.isEmpty(mouseCell)) return;
    
    mouseHex = hexes[mouseCell.getX()][mouseCell.getY()];
    drawHexBorder(mouseHex, Color.CYAN);
  }


  public void initializeImage() {
    double panelWidth  = this.hexBoardPanel.getWidth() * 0.95;  // Available width
    double panelHeight = this.hexBoardPanel.getHeight() * 0.95; // Available height
    double panelSize = Math.min(panelHeight * 1.4 , panelWidth);

    cellWidth = (2 * panelSize )  / (3 * hexBoard.getSize() + 1);
    cellHeight = cellWidth / Math.sin(Math.PI / 3);
    radius = cellHeight / 2;

    createHexes();
    
    // Create the offscreen buffer.
    Dimension dim = hexBoardPanel.getSize();  // get the size of current window
    imageBuffer = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB);
    bufferGraphics = (Graphics2D) imageBuffer.getGraphics();   
    
    bufferGraphics.setStroke(new BasicStroke(2)); // Use a thicker pen
    
    paintFringesTopAndBottom();
    paintFringesLeftAndRight();
    paintCells();
    initialized = true;
  }
  
  
  public Polygon makeHex(double[] cellCenters) {
    int[] vertexX = new int[6];
    int[] vertexY = new int[6];
    for (int v = 0; v < 6; v++) {
      vertexX[v] = (int) (cellCenters[0] + (radius * Math.sin(Math.PI / 3 * v)));
      vertexY[v] = (int) (cellCenters[1] + (radius * Math.cos(Math.PI / 3 * v)));
    }
    return new Polygon(vertexX, vertexY, 6);
  }


  private void markLastMove(double[] cellCenters, Color color) {
    if (HexGameFrame.isDark(color)) bufferGraphics.setColor(Color.WHITE);
    bufferGraphics.fillOval(
            (int)(cellCenters[0]-cellWidth/10), (int)(cellCenters[1]-cellHeight/10), 
            (int)(cellWidth/5),                 (int)(cellHeight/5));

    // If there was a previous last move, erase its center dot.
    Cell prevLastMove = hexBoard.getNextToLastMove();
    if (prevLastMove!= null) paintCell(prevLastMove);
  }


  public void paintCell(Cell cell) {
    int x = cell.getX();
    int y = cell.getY();
    
    double[] cellCenters = cellCenters(x, y);
    Color color = cell.getPlayer().getColor();
    paintHex(hexes[x][y], getRadialPaint(cellCenters, color), true); 
    if (hexBoard.isLastMove(cell)) markLastMove(cellCenters, color);
  }


  private void paintCells() {
	// Looping from 1 to size-2 because the edge cells
	// are painted when we paint the fringes.
    for (int y = 1; y < hexBoardPanel.size-1; y++) {
      for (int x = 1; x < hexBoardPanel.size-1; x++) {
        paintCell(hexBoard.getCell(x, y));
      }
    }
  }
  
  public void paintFringesLeftAndRight() {
    Color color = hexBoard.getLeftRightPlayer().getColor();
    for (int x: new int[]{0, hexBoardPanel.size-1}) {
      for (int y = 0; y < hexBoardPanel.size; y++) {
        double[] cellCenters = cellCenters(x, y);
        cellCenters[0] = cellCenters[0] + (x==0?-5:5);
        cellCenters[1] = cellCenters[1] + (x==0? 2:-2);
        paintHex(makeHex(cellCenters), color, false);
        // Paint the board cell also to prevent image problem.
        paintCell(hexBoard.getCell(x, y));
        if (x == 0) {
          bufferGraphics.setFont(new Font("Arial",Font.PLAIN, (int)(radius * .7 ))) ;
          bufferGraphics.setColor(Color.BLACK);
          bufferGraphics.drawString(Location.charIndices[y]+" -", 
                  (int)cellCenters[0] - (int)(radius * (y < 10 ? 1.8 : 2.1)), 
                  (int)cellCenters[1] + (int)(radius * .3));
        }
      }
    }
  }
  

  public void paintFringesTopAndBottom() {
    Color color = hexBoard.getTopBottomPlayer().getColor();
    for (int y: new int[]{0, hexBoardPanel.size-1}){
      for (int x = 0; x < hexBoardPanel.size; x++) {
        double[] cellCenters = cellCenters(x, y);
        cellCenters[1] = cellCenters[1] + (y==0?-5:5);
        paintHex(makeHex(cellCenters),  color, false);
        // Paint the board cell also to prevent image problem.
        paintCell(hexBoard.getCell(x, y));
        if (y == 0) {
          bufferGraphics.setFont(new Font("Arial",Font.PLAIN, (int)(radius * .7 ))) ;
          bufferGraphics.setColor(Color.BLACK);
          bufferGraphics.drawString(x+"", 
                  (int)cellCenters[0] - (int)(radius * 1.2),  (int)cellCenters[1] - (int)(radius * 1.5));
          bufferGraphics.drawString("\\", 
                  (int)cellCenters[0] - (int)(radius * 0.80), (int)cellCenters[1] - (int)(radius * 0.9));
        }
      }
    }
  }
  
  
  /** drawOutline is true if we are drawing a regular internal hex cell. 
   *  It is false if we are drawing a fringe cell.
   */
  public void paintHex(Polygon hex, Paint paint, boolean isInternalCell) {
    bufferGraphics.setPaint(paint);
    bufferGraphics.fillPolygon(hex);
    if (isInternalCell) drawHexBorder(hex, Color.BLACK);
  }


}
